import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.Timer;

public class Actions {

	public static final Action undoAction = new UndoAction();
	public static final Action redoAction = new RedoAction();
	public static final Action snapAction = new SnapAction();
	public static final Action clearAction = new ClearAction();
	public static final Action renameAction = new RenameAction();
	public static final Action newQuiverAction = new NewQuiverAction();
	public static final Action closeQuiverAction = new CloseQuiverAction();
	public static final Action toggleGridAction = new ToggleGridAction();
	public static final Action toggleLabelAction = new ToggleLabelAction();
	public static final Action loadAnAction = new LoadStandardQuiverAction(StandardQuivers.Type.A),
			                   loadDnAction = new LoadStandardQuiverAction(StandardQuivers.Type.D),
			                   loadEnAction = new LoadStandardQuiverAction(StandardQuivers.Type.E),
			                   loadCycleAction = new LoadStandardQuiverAction(StandardQuivers.Type.CYCLE),
			                   loadWheelAction = new LoadStandardQuiverAction(StandardQuivers.Type.WHEEL),
			                   loadStarAction = new LoadStandardQuiverAction(StandardQuivers.Type.STAR),
			                   loadFriendshipAction = new LoadStandardQuiverAction(StandardQuivers.Type.FRIENDSHIP),
			                   loadCompleteAction = new LoadStandardQuiverAction(StandardQuivers.Type.COMPLETE),
			                   loadPyramidAction = new LoadStandardQuiverAction(StandardQuivers.Type.PYRAMID);
	public static final Action startGAPAction = new StartGAPAction();
	public static final Action saveGAPSessionAction = new SaveGAPSessionAction();
	public static final Action quiverProductGAPAction = new QuiverProductGAPAction();
	public static final Action showGAPInterfaceAction = new ShowGAPInterfaceAction();
	public static final Action sendQuiversGAPAction = new SendQuiversGAPAction();
    public static final Action setupBasicsGAPAction = new SetupBasicsGAPAction();
	public static final Action autoArrangeAction = new AutoArrangeAction();
	public static final Action arrangeAction = new ArrangeAction();
	public static final Action newRepresentationAction = new NewRepresentationAction();

    public static final Action openQuiverAction = new OpenQuiverAction();
    public static final Action saveQuiverAction = new SaveQuiverAction();
    public static final Action openQuiverSessionAction = new OpenQuiverSessionAction();
    public static final Action saveQuiverSessionAction = new SaveQuiverSessionAction();
    public static final Action deleteSelectedNodesAction = new DeleteSelectedNodesAction();
    
    public static final Action saveLaTeXOutputAction = new SaveJTikzOutputAction();

    public static final Action addRelationAction = new AddRelationAction();
    
    public static final Action setFiniteFieldAction = new SetFiniteFieldAction();
    public static final Action resetToRationalsAction = new ResetToRationalsAction();
}

class UndoAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public UndoAction() {
		super("Undo");
		putValue(SHORT_DESCRIPTION, "Undo an operation");
		putValue(MNEMONIC_KEY, KeyEvent.VK_U);
		setEnabled(false);
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		QuiverPanel panel = QuiverFrame.getInstance().getActivePanel();
		panel.undo();
	}
}

class RedoAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public RedoAction() {
		super("Redo");
		putValue(SHORT_DESCRIPTION, "Redo an operation");
		putValue(MNEMONIC_KEY, KeyEvent.VK_R);
		setEnabled(false);
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		QuiverPanel panel = QuiverFrame.getInstance().getActivePanel();
		panel.redo();
	}
}

class SnapAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public SnapAction() {
		super("Snap to grid");
		putValue(SHORT_DESCRIPTION, "Aligns the quiver to the grid");
		putValue(MNEMONIC_KEY, KeyEvent.VK_S);
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		QuiverPanel panel = QuiverFrame.getInstance().getActivePanel();
		panel.quiver.snapToGrid();
		panel.repaint();
	}

}

class ClearAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public ClearAction() {
		super("Clear");
		putValue(SHORT_DESCRIPTION, "Clears the quiver");
		putValue(MNEMONIC_KEY, KeyEvent.VK_C);
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		QuiverFrame.getInstance().getActivePanel().clear();
	}

}

class RenameAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public RenameAction() {
		super("Rename");
		putValue(SHORT_DESCRIPTION, "Renames the quiver");
		putValue(MNEMONIC_KEY, KeyEvent.VK_R);
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		String newName = JOptionPane.showInputDialog(null, "Name:",
				"Enter new name for quiver", JOptionPane.QUESTION_MESSAGE);
		if (newName != null){
			QuiverFrame.getInstance().renameActivePanel(newName);
		}
	}

}

class NewQuiverAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public NewQuiverAction() {
		super("New");
		putValue(SHORT_DESCRIPTION, "Create a new quiver");
		putValue(MNEMONIC_KEY, KeyEvent.VK_C);
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		QuiverFrame.getInstance().addTab(true);
	}
}

class CloseQuiverAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public CloseQuiverAction() {
		super("Close");
		putValue(SHORT_DESCRIPTION, "Close the active quiver");
		putValue(MNEMONIC_KEY, KeyEvent.VK_C);
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		QuiverFrame.getInstance().closeTab();
	}
}

class ToggleGridAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public ToggleGridAction() {
		super("Grid");
		putValue(SHORT_DESCRIPTION, "Toggles grid on/off");
		putValue(MNEMONIC_KEY, KeyEvent.VK_G);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		QuiverPanel panel = QuiverFrame.getInstance().getActivePanel();
		panel.config.gridVisible = !panel.config.gridVisible;
		JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) e.getSource();
		menuItem.setSelected(panel.config.gridVisible);
		panel.repaint();
	}

}

class LoadStandardQuiverAction extends AbstractAction {
	private static final long serialVersionUID = 1L;
	QuiverPanel panel;
	private StandardQuivers.Type type;
	private Integer n;

	public LoadStandardQuiverAction(StandardQuivers.Type type) {
		super(type + "");
		this.type = type;
		putValue(SHORT_DESCRIPTION, "Standard quiver " + type);
	}

	public LoadStandardQuiverAction(StandardQuivers.Type type, int n) {
		super(type + "");
		this.type = type;
		this.n = n;
		putValue(SHORT_DESCRIPTION, "Standard quiver " + type);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		int nValue;
		if (n == null) {
			String nString = JOptionPane.showInputDialog(null, "n=",
					"Enter diagram size", JOptionPane.QUESTION_MESSAGE);
			try {
				nValue = Integer.parseInt(nString);
			} catch (NumberFormatException ex) {
				return;
			}
		} else {
			nValue = n;
		}
		Quiver quiver = StandardQuivers.create(type, nValue);
		if (quiver != null) {
			QuiverFrame frame = QuiverFrame.getInstance();
			frame.addTab(quiver, true);
			frame.reEvaluateNames();
			frame.getActivePanel().repaint();
		} else {
			JOptionPane.showMessageDialog(null, "Invalid number of nodes.",
					"Invalid number of nodes", JOptionPane.ERROR_MESSAGE);
		}
	}

}

class StartGAPAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public StartGAPAction() {
		super("Start GAP");
		putValue(SHORT_DESCRIPTION, "Starts a GAP process");
		putValue(MNEMONIC_KEY, KeyEvent.VK_S);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		QuiverFrame.getInstance().gapInterface = new GAPInterface();
		Actions.quiverProductGAPAction.setEnabled(true);
		Actions.showGAPInterfaceAction.setEnabled(true);
		Actions.sendQuiversGAPAction.setEnabled(true);
		Actions.saveGAPSessionAction.setEnabled(true);
        Actions.setupBasicsGAPAction.setEnabled(true);
		setEnabled(false);
	}

}

class QuiverProductGAPAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public QuiverProductGAPAction() {
		super("Quiver product");
		putValue(SHORT_DESCRIPTION, "Takes the quiver product of two quivers");
		putValue(MNEMONIC_KEY, KeyEvent.VK_P);
		setEnabled(false);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		QuiverFrame quiverFrame = QuiverFrame.getInstance();

		ArrayList<Quiver> quivers = quiverFrame.getQuivers();
		String[] quiverNames = new String[quivers.size()];
		for (int i = 0; i < quivers.size(); i++) {
			quiverNames[i] = quivers.get(i).getName();
		}
		Quiver activeQuiver = quiverFrame.getActivePanel().quiver;

		Quiver q1 = (Quiver) JOptionPane.showInputDialog(quiverFrame,
				"Choose first quiver:", "Quiver product",
				JOptionPane.PLAIN_MESSAGE, null, quivers.toArray(),
				activeQuiver);
		if (q1 == null) {
			return;
		}
		Quiver q2 = (Quiver) JOptionPane.showInputDialog(quiverFrame,
				"Choose second quiver:", "Quiver product",
				JOptionPane.PLAIN_MESSAGE, null, quivers.toArray(),
				activeQuiver);
		if (q2 == null) {
			return;
		}
		Quiver productQuiver = quiverFrame.gapInterface.quiverProduct(q1, q2);
		quiverFrame.addTab(productQuiver, true);
	}
}

class SendQuiversGAPAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public SendQuiversGAPAction() {
		super("Send quivers");
		putValue(SHORT_DESCRIPTION, "Sends the quivers to GAP");
		putValue(MNEMONIC_KEY, KeyEvent.VK_P);
		setEnabled(false);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		QuiverFrame quiverFrame = QuiverFrame.getInstance();
		for (Quiver quiver : quiverFrame.getQuivers()) {
			quiverFrame.gapInterface.sendQuiver(quiver);
		}
	}
}

class SetupBasicsGAPAction extends AbstractAction {
    private static final long serialVersionUID = 1L;

    public SetupBasicsGAPAction() {
        super("Setup Basics");
        putValue(SHORT_DESCRIPTION, "Sets up basic stuff in GAP");
        putValue(MNEMONIC_KEY, KeyEvent.VK_P);
        setEnabled(false);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        QuiverFrame.getInstance().gapInterface.setupBasicsInGAP();
    }
}

class AutoArrangeAction extends AbstractAction {
	private static final long serialVersionUID = 1L;
	private Timer timer;

	public AutoArrangeAction() {
		super("Auto-arrange quiver");
		putValue(SHORT_DESCRIPTION, "Toggle automatically arranging quiver");
		putValue(MNEMONIC_KEY, KeyEvent.VK_A);
		Configuration config = Configuration.getDefaultConfiguration();
		ActionListener updateListener = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				QuiverPanel panel = QuiverFrame.getInstance().getActivePanel();
				panel.moveAutoArrangeQuiverToHeldNode();
				panel.repaint();
			}

		};
		timer = new Timer(config.autoArrangeUpdateDelay, updateListener);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) e.getSource();
		Configuration config = Configuration.getDefaultConfiguration();
		if (config.autoArrangeQuiver) {
			timer.stop();
		} else {
			timer.start();
		}
		config.autoArrangeQuiver = !config.autoArrangeQuiver;
		menuItem.setSelected(config.autoArrangeQuiver);
		Actions.snapAction.setEnabled(!config.autoArrangeQuiver);
	}

}

class ArrangeAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public ArrangeAction() {
		super("Arrange");
		putValue(SHORT_DESCRIPTION, "Arrange quiver");
		putValue(MNEMONIC_KEY, KeyEvent.VK_A);
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		Configuration config = Configuration.getDefaultConfiguration();
		QuiverPanel panel = QuiverFrame.getInstance().getActivePanel();
		for (int i = 0; i < config.arrangeSteps; i++) {
			panel.moveAutoArrangeQuiverToHeldNode();
		}
		panel.repaint();
	}
}

class ToggleLabelAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public ToggleLabelAction() {
		super("Labels");
		putValue(SHORT_DESCRIPTION, "Toggle displaying labels");
		putValue(MNEMONIC_KEY, KeyEvent.VK_L);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) e.getSource();
		Configuration config = Configuration.getDefaultConfiguration();
		config.showLabels = !config.showLabels;
		menuItem.setSelected(config.showLabels);
		QuiverFrame.getInstance().getActivePanel().repaint();
	}
}

class ShowGAPInterfaceAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public ShowGAPInterfaceAction() {
		super("Interface");
		putValue(SHORT_DESCRIPTION, "Show GAP interface");
		putValue(MNEMONIC_KEY, KeyEvent.VK_I);
		setEnabled(false);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		QuiverFrame.getInstance().gapInterface.showInterface();
	}
}

class SaveGAPSessionAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public SaveGAPSessionAction() {
		super("Save GAP session");
		putValue(SHORT_DESCRIPTION, "Save GAP session");
		putValue(MNEMONIC_KEY, KeyEvent.VK_S);
		setEnabled(false);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		final JFileChooser filechooser = new JFileChooser();
		QuiverFrame quiverFrame = QuiverFrame.getInstance();
		int returnVal = filechooser.showSaveDialog(quiverFrame);
		if (returnVal != JFileChooser.APPROVE_OPTION){
			return;
		}
		quiverFrame.gapInterface.writeInputLogToFile(filechooser.getSelectedFile());
	}
}

class NewRepresentationAction extends AbstractAction {
	private static final long serialVersionUID = 1L;

	public NewRepresentationAction() {
		super("New representation");
		putValue(SHORT_DESCRIPTION, "Create a new representation of the active quiver");
		putValue(MNEMONIC_KEY, KeyEvent.VK_N);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		QuiverPanel quiverPanel = QuiverFrame.getInstance().getActivePanel();
		quiverPanel.addRepresentation();
	}
}

class OpenQuiverAction extends  AbstractAction {
    private static final long serialVersionUID =1L;

    public OpenQuiverAction() {
        super("Open quiver");
        putValue(SHORT_DESCRIPTION, "Open quiver");
        putValue(MNEMONIC_KEY, KeyEvent.VK_O);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        final JFileChooser fileChooser = new JFileChooser();
        fileChooser.setFileFilter(new QuiverFilter());
        fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

        QuiverFrame quiverFrame = QuiverFrame.getInstance();
        QuiverPanel quiverPanel = null;
        int returnVal = fileChooser.showOpenDialog(quiverFrame);
        if (returnVal != JFileChooser.APPROVE_OPTION){
            return;
        }
        quiverPanel = FileHandler.openQuiverFile(fileChooser.getSelectedFile());
        quiverFrame.addTab(quiverPanel,  true);
    }
}

class OpenQuiverSessionAction extends AbstractAction {
    private static final long serialVersionUID =1L;

    public OpenQuiverSessionAction() {
        super("Open saved session");
        putValue(SHORT_DESCRIPTION, "Open saved session");
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        final JFileChooser fileChooser = new JFileChooser();
        fileChooser.setFileFilter(new QuiverSessionFilter());
        fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        QuiverFrame quiverFrame = QuiverFrame.getInstance();
        ArrayList<QuiverPanel> panels= null;
        int returnVal = fileChooser.showOpenDialog(quiverFrame);
        if (returnVal != JFileChooser.APPROVE_OPTION){
            return;
        }
        panels = FileHandler.openQuiverSessionFile(fileChooser.getSelectedFile());
        if (panels!= null) {
            for (QuiverPanel panel: panels) {
                quiverFrame.addTab(panel, true);
            }
        }
    }
}

class SaveQuiverSessionAction extends AbstractAction {
    private static final long serialVersionUID =1L;

    public SaveQuiverSessionAction() {
        super("Save current session");
        putValue(SHORT_DESCRIPTION, "Save current session");
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        final JFileChooser fileChooser = new JFileChooser();
        fileChooser.setFileFilter(new QuiverSessionFilter());
        fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        QuiverFrame quiverFrame = QuiverFrame.getInstance();
        int returnVal = fileChooser.showSaveDialog(quiverFrame);
        if (returnVal != JFileChooser.APPROVE_OPTION){
            return;
        }
        FileHandler.saveQuiverSessionFile(fileChooser.getSelectedFile(), quiverFrame.getQuiverPanels());
    }
}

class SaveQuiverAction extends AbstractAction {
    private static final long serialVersionUID =1L;

    public SaveQuiverAction() {
        super("Save active quiver");
        putValue(SHORT_DESCRIPTION, "Save active quiver");
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        final JFileChooser fileChooser = new JFileChooser();
        fileChooser.setFileFilter(new QuiverFilter());
        fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        QuiverFrame quiverFrame = QuiverFrame.getInstance();
        int returnVal = fileChooser.showSaveDialog(quiverFrame);
        if (returnVal != JFileChooser.APPROVE_OPTION){
            return;
        }
        FileHandler.saveQuiverFile(fileChooser.getSelectedFile(), quiverFrame.getActivePanel());
    }
}

class SaveJTikzOutputAction extends AbstractAction {
    private static final long serialVersionUID =1L;

    public SaveJTikzOutputAction() {
        super("Save LaTeX output");
        putValue(SHORT_DESCRIPTION, "Save LaTeX output");
    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        final JFileChooser fileChooser = new JFileChooser();
        fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        QuiverFrame quiverFrame = QuiverFrame.getInstance();
        int returnVal = fileChooser.showSaveDialog(quiverFrame);
        if (returnVal != JFileChooser.APPROVE_OPTION){
            return;
        }
        FileHandler.saveJTikzOutput(fileChooser.getSelectedFile(), quiverFrame.getActivePanel());
    }
}

class AddRelationAction extends AbstractAction {
    private static final long serialVersionUID =1L;

    public AddRelationAction() {
        super("Add relation");
        putValue(SHORT_DESCRIPTION, "Add relation to active quiver");
    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        if(!QuiverFrame.getInstance().getActivePanel().addRelation(QuiverFrame.getInstance().getRelationString())) {
            JOptionPane.showMessageDialog(QuiverFrame.getInstance(), "Relation already exists", "BORK!!",
                    JOptionPane.ERROR_MESSAGE);
        }
    }
}

class DeleteSelectedNodesAction extends AbstractAction {

    @Override
    public void actionPerformed(ActionEvent actionEvent) {
    QuiverFrame.getInstance().getActivePanel().deleteSelectedNodes();
    QuiverFrame.getInstance().repaint();
    }
}

class SetFiniteFieldAction extends AbstractAction {
    private static final long serialVersionUID =1L;

    public SetFiniteFieldAction() {
        super("Set finite field");
        putValue(SHORT_DESCRIPTION, "Set finite field");
    }
    
    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        QuiverFrame.getInstance().getActivePanel().setFiniteField();
    }
}

class ResetToRationalsAction extends AbstractAction {
    
    public ResetToRationalsAction() {
        super("Reset to Rationals");
        putValue(SHORT_DESCRIPTION, "Reset to Rationals");
    }
    
    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        QuiverFrame.getInstance().getActivePanel().resetFieldToRationals();
    }
}